package cn.openea.perpartner.wxpay.utils;

import org.springframework.util.DigestUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.*;
import java.lang.reflect.Modifier;
/**
 * @codeDesc:公共组件函数
 * @author: Mr.Yun
 * @QQ: 2034374624
 * @version: V1.0
 * @Created: 2021/08/18 17:10
 */
public class Tools {

    /**
     * 初始化使系统支持256位秘钥加解密
     */
    static {
        Field field = null;
        try {
            field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
            field.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            field.set(null, java.lang.Boolean.FALSE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static IdWorker worker = new IdWorker(2,1,1);

    /**
     * 获取用户ID身份
     *  格式：yyMMddhhmmssSSS+3位随机数
     * @return 返回用户身份id，保证id唯一
     */
    public static String autoUserID() {
        return autoUserID(3);
    }

    /**
     * 获取用户ID身份
     *  格式：yyMMddhhmmssSSS+randomSize位随机数
     * @param randomSize 定制id后随机的数字个数
     * @return 返回获取的身份ID
     */
    public static String autoUserID(int randomSize) {
        String sformat = "yyMMddHHmmssSSS";
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat(sformat);
        String dateString = formatter.format(currentTime);
        String numStr = "";
        for (int i = 0; i < randomSize; i++) {
            numStr += (int) (10 * (Math.random()));
        }
        String thisUserid = dateString + numStr;
        return thisUserid;
    }

    /**
     * 获取一个用户id，长度24位
     *  该方试验证100次，每次使用循环获取1kw个userid均无出现重复的情况，所以可靠性极高，基本无需验证userid是否重复
     * @return
     * @throws Exception
     */
    public static String getNewUserid() {
        // 指定用户名中包含的时间信息
        String sformat = "yyyyMMddHHmmss";
        SimpleDateFormat formatter = new SimpleDateFormat(sformat);
        Date currentTime = new Date();
        String dateString = formatter.format(currentTime);
        // 将时间信息转换成十六进制，这里是12位
        String hexString = Long.toHexString(Long.parseLong(dateString));
        int cost = hexString.length()/3;
        // 4 随机字母 + 4位前日期 + 4位随机数字和字母 + 4位中日期 + 4位纯数字 + 4位末日期  共24位长度的用户
        String a1 = getRandom(0, 4);
        String a2 = hexString.substring(0, cost);
        String a3 = getRandom(2, 4);
        String a4 = hexString.substring(cost, cost*2);
        String a5 = getRandom(1, 4);
        String a6 = hexString.substring(cost*2);
        String userid = (a1+a2+a3+a4+a5+a6).toUpperCase();
        return userid;
    }

    /**
     * 获取指定类型的随机字符长度
     * @param type 类型，0表示大写字母，1表示数字，2表示数字与字母混合
     *             如果传类型错误，则默认值为2
     * @param length 长度
     * @return
     */
    public static String getRandom(int type, int length) {
        if(type < 0 || type > 3){
            type = 2;
        }
        StringBuffer sbf = new StringBuffer();
        List<String> charList = Arrays.asList("ABCDEFGHJKLMNOPQRSTUVWXYZ","0123456789","0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        String allChar = charList.get(type);
        int charlength = allChar.length();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sbf.append(allChar.charAt(random.nextInt(charlength)));
        }
        return sbf.toString();
    }


    /**
     * 获取单号
     *  格式：单号前辍+yyMMddhhmmssSSS+3位随机数字
     * @param prefixstr 单号前辍
     * @return 生成的单号
     */
    public static String autoDocNumber(String prefixstr) {
        return autoDocNumber(null, prefixstr, 3);
    }

    /**
     * 获取指定个数的连续单号
     *  格式：单号前辍+yyMMddhhmmssSSS+3位随机数字
     * @param prefixstr 单号前辍
     * @param docNumberSize 指定的单号个数
     * @return 生成指定个数的连续单号
     */
    public static List<String> autoDocNumberList(String prefixstr, int docNumberSize) {
        return autoDocNumberList(prefixstr, 3, docNumberSize);
    }

    /**
     * 获取指定个数的连续单号
     *  格式：单号前辍+yyMMddhhmmssSSS+randomSize位随机数字
     * @param prefixstr 单号前辍
     * @param randomSize 定制单号后随机的数字个数
     * @param docNumberSize 指定的单号个数
     * @return 生成指定个数的连续单号
     */
    public static List<String> autoDocNumberList(String prefixstr, int randomSize, int docNumberSize) {
        List<String> docNumberList = new ArrayList<>();
        String tmpNumber = "";
        for (int i = 0; i < docNumberSize; i++) {
            tmpNumber = autoDocNumber(tmpNumber, prefixstr, 3);
            docNumberList.add(tmpNumber);
        }
        return docNumberList;
    }

    /**
     * 公用-生成18位需求编号
     * @param docNumber 如果是空单号则生成一个新单号，若不为空则在原单号的基础上生成自增单号
     *  注意，生成自增单号数量若randomSize=3，不宜超过 999999 个，最大只能支持自增 999999999999999 个，若要加大单号量可将randomSize的值设置得更大一些
     * @param prefixstr 单号前辍
     * @param randomSize 单号后随机几个数字 一般请传入3
     * @return 按要求生成的单号
     */
    public static String autoDocNumber(String docNumber, String prefixstr, int randomSize) {
        String newDocNumber = "";
        if(Tools.isBlank(docNumber)){
            // 如果是空单号，则生成一个单号
            newDocNumber = prefixstr + autoUserID(randomSize);
        } else {
            // 如果是有单号，则在原单号的基础上自增单号
            try {
                String tmpDocNumber = docNumber.substring(prefixstr.length());
                Long longnum = Long.parseLong(tmpDocNumber);
                // 单号自增
                longnum++;
                String logstr = String.valueOf(longnum);
                // 在前面补齐0
                if(logstr.length() < tmpDocNumber.length()){
                    int zorelen = tmpDocNumber.length() - logstr.length();
                    for (int i = 0; i < zorelen; i++) {
                        logstr = 0 + logstr;
                    }
                }
                newDocNumber = prefixstr + logstr;
            } catch (Exception e){
                // 如果出错，则按无传入单号的方式再生成一个单号
                newDocNumber = autoDocNumber(null, prefixstr, randomSize);
            }
        }
        return newDocNumber;
    }

    /**
     * 获取唯一ID，重复概率极极极小，19位长度的long，19位长度的long
     * 本机机器id=1
     * ID具有渐大（类自增）且具有排序性，基于Snowflake算法进行生成
     * @return
     */
    public static long getNewUnid() {
        return worker.nextId();
    }

    /**
     * 判断字符串是否为空值或去掉前后空格长度为0
     *
     * @param string 要判断的字符串
     * @return 判断结果，true为空，false为非空
     */
    public static boolean isBlank(String string) {
        return (isEmpty(string)) || (string.trim().length() == 0);
    }

    /**
     * 判断字符串是否不为空值
     *
     * @param string 要判断的字符串
     * @return 判断结果，true为非空，false为空
     */
    public static boolean isNotBlank(String string) {
        return !isBlank(string);
    }

    /**
     * 判断字符串是否为null或"null"或长度为0
     *
     * @param string 要判断的字符串
     * @return 判断结果，true为空，false为非空
     */
    private static boolean isEmpty(String string) {
        return (string == null) || (string.equals("null")) || (string.length() == 0);
    }

    /**
     * 获得一个给定长度的随机字符串
     *
     * @param length 随机字符串的长度
     * @return 随机字符串
     */
    public static String getRandom(int length) {
        String allChar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        StringBuffer sb = new StringBuffer();
        int charlength = allChar.length();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(allChar.charAt(random.nextInt(charlength)));
        }
        return sb.toString();
    }

    /**
     * md5加密函数
     *
     * @param str 要加密的字符串
     * @return 加密后的字符串
     */
    public static String md5(String str) {
        return DigestUtils.md5DigestAsHex(str.getBytes());
    }

    /**
     * 对字符串进行base64编码
     * @param str 待编码字符串
     * @return 编码字符串
     */
    public static String base64(String str) {
        // base64字符串
        String base64Str = "";
        try {
            byte[] data = str.getBytes("utf-8");
            base64Str = Base64.getEncoder().encodeToString(data);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return base64Str;
    }

    /**
     * 对字符串进行base64解码
     * @param base64Str 待解码字符串
     * @return 解码字符串
     */
    public static String unBase64(String base64Str) {
        String str = "";
        byte[] base64Data = Base64.getDecoder().decode(base64Str);
        try {
            str = new String(base64Data, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 调用解密算法对微信数据进行 AES-256-ECB解密（PKCS7Padding）
     * @param base64Data 微信返回的req_info数据
     * @param lowMad5Key 商户key做md5后的小写32位加密串
     * @return
     * @throws Exception
     */
    public static String decryptWxData(String base64Data, String lowMad5Key) throws Exception {
        SecretKeySpec key = new SecretKeySpec(lowMad5Key.getBytes(), "AES");// 密钥算法
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");//加解密算法/工作模式/填充方式
        cipher.init(Cipher.DECRYPT_MODE, key);
        Base64.Decoder decoder = Base64.getDecoder();
        String newData= new String(decoder.decode(base64Data), "ISO-8859-1");
        return new String(cipher.doFinal(newData.getBytes("ISO-8859-1")), "utf-8");
    }

    public static String readRequestBodyData(HttpServletRequest request) {
        BufferedReader reader = null;
        try {
            StringBuilder bodydata = new StringBuilder();
            reader = request.getReader();
            for (String line; (line = reader.readLine()) != null;){
                if (bodydata.length() > 0){
                    bodydata.append("\n");
                }
                bodydata.append(line);
            }
            return bodydata.toString();
        } catch (IOException e) {
            throw new RuntimeException();
        } finally {
            reader = null;
        }
    }

}
